/*
 * 创建QVue类，接收一个options对象
 */
class QVue{
	//构造方法
	constructor(options){
		/*缓存option对象数据     $是为了防止命名污染*/
		this.$options = options;
		/*
		 * opthins====
		 * {
		 *		el:"#app",
		 * 		data:{
		 * 			name:"name",
		 * 			age:12
		 * }	
		 */
		
		
		/*取出data数据做数据响应    =====》理解Vue中通过this.$data可以取所有我们写的data数据*/
		this.$data = options.data||{};/*data不为空则取data，data为空则取一个空对象*/
		/*短路或*/
		/*避免$data为undefined*/
		
		//监听数据的变化
		this.observe(this.$data);
	}
	
	//观察数据变化
	observe(data){
		//$data不存在或不是对象类型则不予监听
		/*{}[]都是object*/
		if(!data|| typeof data !== "object"){
			return;
		}
		
		//取所有的键(数组的话取出下标)   ["name","age"]
		let keys = Object.keys(data);//keys==Object内置对象方法，返回一个对象自身可枚举属性组成数组
		
		//循环每一个建
		keys.forEach((key)=>{
			
			//循环每一个对象中的键值对
			//数据响应			对象     属性    属性值
			this.defineReactive(data,key,data[key]);
			
			//代理data中的属性到vue实例上    
			//model中有的view-model中也要一份   model中的真实数据映射一份到view-model中
			this.proxyData(key);
		})
	}
	
	//数据响应   操作$data   model
	//   		  $data name "name"
	//   		  $data age "12"
	//给每个属性添加一个set get函数==》感知数据变化
	defineReactive(data,key,val){
		//解决数据层次嵌套，递归绑定监听    $data中属性下还是一个对象
		/*如：
		 * data:{
		 * 		name:"name",
		 * 		addr:{
		 * 			pro:"湖南",
		 * 			city:"长沙"
		 * 		}
		 * }
		 * 若不循环，则监听的是addr对象，只有对象地址改变才监听得到，当值改变pro="北京",对象得地址并没有改变，所以监听不到
		 */
		this.observe(val);
		
		//管理watcher:(请求和响应与谁)
		const dep= new Dep();
		
		//此处data指$data对象/*es6引擎---监听绑定    基于es6的js引擎*/
		//开始绑定   开始数据监听了      对象，属性名
		Object.defineProperty((data,key,{
			get(){//获取当前key对应的值
				//向管理watcher的对象追加watcher实例，方便管理，每一个都要绑定监听
				//target监听发给谁 必须要有目标对象，然后就是添加Watcher ============= Watcher 连接 Observer
				
				//添加对应Watcher
				Dep.target && dep.addWatcher(Dep.target);
				//返回当前属性的值
				return val;
			},
			set(val){
				//值没有变化
				if(newVal===val){
					return;
				}
				//只有值发生变化才发出更新通知
				val=newVal;
				
				//通知对应watcher起作用
				dep.notify();
			}
		}))
	}
	
	//代理data    复制一份
	proxyData(key){//键名
		//this指vue对象 == app，key指$data中的每个键   
		//view-model中也生成一套相同的model    双向绑定
		Object.defineProperty(this,key,{
			get(){
				return this.$data[key];//前面$data中的都绑定了set get 此处就相当于调用了$data中的get方法
			},
			set(){
				this.$data[key] = newVal;//此处就相当于调用了$data中的set方法
			}
		})
	}
	//在view-model中同样生成一套set get方法，联系起来
}

//管理watcher
class Dep{
	constructor() {
	    //储存每个属性对应的$data属性对应的watcher
	    this.watchers=[];//初始化    数组存储多个watcher
	}
	
	//添加
	addWatcher(watcher){
		this.watchers.push(watcher);
	}
	
	//通知所有watcher更新
	notify(){
		this.watchers.forEach((watcher)=>{
			watcher.updata();//通知更新
		})
	}
}

//观察者，具体更新
class Watcher{
	constructor(vm,key,func) {
		//vue实例
	    this.vm = vm;
	    //需要更新的key
	    this.key=key;
	    //更新后执行的函数
	    this.func=func;
	    
	    //将当前watcher实例指定到Dep静态属性target，用于类间通信
	    Dep.target = this;
	    //触发get,添加依赖
	    this.vm[this.key];
	    Dep.target=null;
	}
	
	updata(){
		this.func.call(this.vm,this.vm[this.key]);
	}
}
